package com.jzy.gulimall.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzy.common.constant.OrderEnum;
import com.jzy.common.exception.BizCodeEnum;
import com.jzy.common.exception.NoStockException;
import com.jzy.common.to.SkuHasStockRespVo;
import com.jzy.common.to.mq.OrderTo;
import com.jzy.common.to.mq.SeckillOrderTo;
import com.jzy.common.utils.PageUtils;
import com.jzy.common.utils.Query;
import com.jzy.common.utils.R;
import com.jzy.common.vo.MemberRespVo;
import com.jzy.gulimall.order.constant.OrderConstant;
import com.jzy.gulimall.order.dao.OrderDao;
import com.jzy.gulimall.order.entity.OrderEntity;
import com.jzy.gulimall.order.entity.OrderItemEntity;
import com.jzy.gulimall.order.entity.PaymentInfoEntity;
import com.jzy.gulimall.order.enume.OrderCreateEnum;
import com.jzy.gulimall.order.enume.OrderDeleteEnum;
import com.jzy.gulimall.order.enume.OrderStatusEnum;
import com.jzy.gulimall.order.feign.CartFeignService;
import com.jzy.gulimall.order.feign.MemberFeignService;
import com.jzy.gulimall.order.feign.ProductFeignService;
import com.jzy.gulimall.order.feign.WareFeignService;
import com.jzy.gulimall.order.interceptor.UserLoginInterceptor;
import com.jzy.gulimall.order.service.OrderItemService;
import com.jzy.gulimall.order.service.OrderService;
import com.jzy.gulimall.order.service.PaymentInfoService;
import com.jzy.gulimall.order.to.OrderCreatTo;
import com.jzy.gulimall.order.util.RedisUtil;
import com.jzy.gulimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Resource
    private MemberFeignService memberFeignService;

    @Resource
    private CartFeignService cartFeignService;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private WareFeignService wareFeignService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private ProductFeignService productFeignService;

    @Resource
    private OrderItemService orderItemService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private PaymentInfoService paymentInfoService;

    private ThreadLocal<OrderSubmitVo> threadLocal = new ThreadLocal<>();

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 订单确认页
     * 获取订单确认信息
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberRespVo memberRespVo = UserLoginInterceptor.loginUser.get();
        //1异步编排 开启新线程  RequestContextHolder使用threadlocal保存请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            //远程获取所有收货地址
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            orderConfirmVo.setMemberAddressVos(address);
        }, threadPoolExecutor);

        CompletableFuture<Void> getUserCartItemFuture = CompletableFuture.runAsync(() -> {
            //远程获取所有用户购物项
            //feign在远程调用之前会构造请求 会构造很多拦截器
            //加上feign请求拦截器  共享RequestContextHolder
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderCartItemVo> userCartItem = cartFeignService.getUserCartItem();
            orderConfirmVo.setOrderCartItemVos(userCartItem);
        }, threadPoolExecutor).thenRunAsync(() -> {
            //远程获取商品库存
            List<OrderCartItemVo> orderCartItemVos = orderConfirmVo.getOrderCartItemVos();
            if (CollectionUtil.isNotEmpty(orderCartItemVos)) {
                List<Long> skuIds = orderCartItemVos.stream().map(OrderCartItemVo::getSkuId).collect(Collectors.toList());
                R skuHasStock = wareFeignService.getSkuHasStock(skuIds);
                List<SkuHasStockRespVo> stockRespVo = skuHasStock.getData(new TypeReference<List<SkuHasStockRespVo>>() {
                });
                if (CollectionUtil.isNotEmpty(stockRespVo)) {
                    Map<Long, Boolean> collect = stockRespVo.stream().collect(Collectors.toMap(SkuHasStockRespVo::getSkuId, SkuHasStockRespVo::getHasStock));
                    orderConfirmVo.setHasStock(collect);
                }
            }
        }, threadPoolExecutor);
        //用户积分
        Integer integration = memberRespVo.getIntegration();
        orderConfirmVo.setIntegration(integration);
        //价格自动计算
        //防止重复提交订单,生成一个防重令牌
        String orderToken = UUID.randomUUID().toString(true);
        orderConfirmVo.setOrderToken(orderToken);
        redisUtil.set(OrderConstant.ORDER_USER_TOKEN + memberRespVo.getId(), orderToken, 30 * 60);
        CompletableFuture.allOf(getAddressFuture, getUserCartItemFuture).get();
        if (CollectionUtil.isEmpty(orderConfirmVo.getOrderCartItemVos())) {
            return null;
        }
        return orderConfirmVo;
    }

    /**
     * 提交订单  生成订单
     * 创建订单,校验令牌,校验价格,锁库存
     * 本地事务存在的问题:1.远程服务假失败  2.其他服务出现问题,已执行的远程服务不能回滚
     * 坑: 本地事务基于代理类实现  this.()调用本类其他方法, 没有调用其他代理类的Transactional方法  导致事务传播级别失效
     * 坑: 同一个代理类方法互调,绕过了代理类  解决方法:通过代理对象来调用
     * 1.引入aop-start   @EnableAspectJAutoProxy(exposeProxy = true):exposeProxy暴露代理对象
     * 2.Object o = AopContext.currentProxy();
     * <p>
     * 分布式事务解决方案
     * 1. 2pc协议/3pc协议 刚`
     * 2. tcc柔性事务补偿性方案 柔性事务,遵循base理论,最终一致性  seata
     * 允许一段时间内,不同的节点数据不一致,但要求最终一致
     * 3. 柔性事务-最大努力通知方案 支付宝支付成功异步回调 通知失败既回滚
     * 4. 柔性事务-可靠消息+最终一致性方案
     */
    //seata 默认AT模式,不适合高并发的场景
    //@GlobalTransactional
    @Override
    @Transactional
    public OrderRespVo submitOrder(OrderSubmitVo orderSubmitVo) {
        OrderRespVo orderRespVo = new OrderRespVo();
        threadLocal.set(orderSubmitVo);
        //1.校验令牌
        MemberRespVo memberRespVo = UserLoginInterceptor.loginUser.get();
        String orderToken = orderSubmitVo.getOrderToken();
        Long result = redisUtil.getAndDelLua(OrderConstant.ORDER_USER_TOKEN + memberRespVo.getId(), orderToken);
        if (result == 0) {
            orderRespVo.setCode(OrderCreateEnum.REPEAT.getCode());
            return orderRespVo;
        } else {
            //令牌验证成功
            //2.创建订单
            OrderCreatTo orderCreatTo = CreatOrder();
            //校验价格
            BigDecimal payPrice = orderSubmitVo.getPayPrice();
            BigDecimal payAmount = orderCreatTo.getOrderEntity().getPayAmount();
            if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
                //金额对比,小于0.01
                //3.保存订单
                saveOrder(orderCreatTo);
                //4.远程锁库存 只有有异常,就回滚
                WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
                wareSkuLockVo.setOrderSn(orderCreatTo.getOrderEntity().getOrderSn());   //订单号
                //可以直接从上面获取 ,放入threadlocal中
                List<OrderCartItemVo> locksVo = orderCreatTo.getOrderItemEntities().stream().map(item -> {
                    OrderCartItemVo orderCartItemVo = new OrderCartItemVo();
                    orderCartItemVo.setCount(item.getSkuQuantity()); //锁定的商品数量
                    orderCartItemVo.setSkuId(item.getSkuId());  //锁的商品skuId
                    orderCartItemVo.setTitle(item.getSkuName());
                    return orderCartItemVo;
                }).collect(Collectors.toList());
                wareSkuLockVo.setLocks(locksVo);
                R r = wareFeignService.lockStock(wareSkuLockVo);
                //为了保证高并发,库存服务自己回滚,发消息给库存服务
                //库存服务 本身也可以使用自动解锁模式
                if (r.getCode() == 0) {
                    //锁定成功
                    orderRespVo.setCode(OrderCreateEnum.SUCCESS.getCode());
                    orderRespVo.setOrderEntity(orderCreatTo.getOrderEntity());
                    //int i = 10 / 0;
                    // TODO: 2020/9/23  订单生成成功,删除购物车
                    //订单创建成功,发送消息到mq
                    rabbitTemplate.convertAndSend(OrderConstant.ORDER_EVENT_EXCHANGE, OrderConstant.ORDER_CREAT_ROUTING_KEY, orderCreatTo.getOrderEntity());
                    return orderRespVo;
                } else {
                    //锁定失败
                    //抛异常,回滚订单
                    throw new NoStockException(BizCodeEnum.NO_STOCK_EXCEPTION.getMessage());
                }
            } else {
                orderRespVo.setCode(OrderCreateEnum.PRICE_ERROR.getCode());
                return orderRespVo;
            }
        }
    }

    /**
     * 远程调用 解锁库存 查看订单状态
     * 根据订单号查询订单信息
     */
    @Override
    public OrderEntity getOrderStatus(String orderSn) {
        return baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }

    /**
     * 订单过期 关闭订单
     */
    @Override
    public void closeOrder(OrderEntity entity) {
        //查询订单状态
        OrderEntity orderEntity = baseMapper.selectById(entity.getId());
        if (orderEntity.getStatus().equals(OrderEnum.CREATE_NEW.getCode())) {
            //关单
            OrderEntity update = new OrderEntity();
            update.setId(entity.getId());
            update.setStatus(OrderEnum.CANCLED.getCode());
            baseMapper.updateById(update);
            //订单解锁之后发送消息库存,防止库存在订单之前触发解锁
            OrderTo orderTo = new OrderTo();
            BeanUtil.copyProperties(entity, orderTo);
            rabbitTemplate.convertAndSend(OrderConstant.ORDER_EVENT_EXCHANGE, OrderConstant.ORDER_RELEASE_WARE_ROUTING_KEY, orderTo);
        }
    }

    /**
     * aiPay支付
     * 根据订单号获取订单信息
     */
    @Override
    public PayVo getOrderPayInfo(String orderSn) {
        OrderEntity orderEntity = getOrderStatus(orderSn);
        if (orderEntity != null && orderEntity.getStatus().equals(OrderEnum.CREATE_NEW.getCode())) {
            PayVo payVo = new PayVo();
            payVo.setOutTradeNo(orderEntity.getOrderSn());  //订单号
            BigDecimal decimal = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP); //向上取整
            payVo.setTotalAmount(decimal.toString());   //订单金额
            List<OrderItemEntity> orderItemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
            OrderItemEntity orderItemEntity = orderItemEntities.get(0);
            payVo.setSubject(orderItemEntity.getSkuName());
            payVo.setBody(orderItemEntity.getSpuName());
            return payVo;
        }
        return null;
    }

    /**
     * 会员服务远程调用 查询会员的所有订单
     */
    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberRespVo memberRespVo = UserLoginInterceptor.loginUser.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", memberRespVo.getId()).orderByDesc("id")
        );
        List<OrderEntity> records = page.getRecords();
        List<OrderEntity> orderEntities = records.stream().map(item -> {
            List<OrderItemEntity> orderItemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", item.getOrderSn()));
            item.setOrderItemEntities(orderItemEntities);
            return item;
        }).collect(Collectors.toList());
        page.setRecords(orderEntities);
        return new PageUtils(page);
    }

    /**
     * aiPay支付
     * 异步回调 保存交易流水 修改订单状态信息
     */
    @Override
    public String handlerAliPayed(PayAsyncVo payAsyncVo) {
        String trade_status = payAsyncVo.getTrade_status();
        String out_trade_no = payAsyncVo.getOut_trade_no();
        //1.保存交易流水
        PaymentInfoEntity infoEntity = new PaymentInfoEntity();
        infoEntity.setAlipayTradeNo(payAsyncVo.getTrade_no());  //支付宝交易流水号
        infoEntity.setOrderSn(out_trade_no);    //订单号
        infoEntity.setTotalAmount(new BigDecimal(payAsyncVo.getInvoice_amount()));//支付总金额
        infoEntity.setSubject(payAsyncVo.getSubject()); //交易内容
        infoEntity.setPaymentStatus(trade_status);//支付状态
        infoEntity.setCreateTime(new Date());//创建时间
        infoEntity.setCallbackTime(payAsyncVo.getNotify_time());//回调时间
        paymentInfoService.save(infoEntity);
        //2.修改订单状态信息
        if (trade_status.equals(OrderConstant.TRADE_SUCCESS) || trade_status.equals(OrderConstant.TRADE_FINISHED)) {
            /**
             * 支付-订单-库存
             * 问题: 订单到期,库存解锁,   然后支付成功:
             *      解决:使用支付宝自动收单,一段时间不支付就不能支付
             *           问题:提交订单之后没有去支付页面
             *                  解决:根据订单的创建时间设置失效时间
             * 问题: 支付成功,没有及时回调修改状态,导致订单到期,库存解锁
             *      解决: 订单取消之后,手动调用关闭支付宝支付,通知支付宝退钱
             *
             *  下载支付宝账单,进行对账
             */
            baseMapper.updateOrderStatus(out_trade_no, OrderEnum.PAYED.getCode());
        }
        return "success";
    }

    /**
     * 创建秒杀订单
     */
    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        //保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        orderEntity.setStatus(OrderEnum.CREATE_NEW.getCode());
        BigDecimal multiply = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum()));
        orderEntity.setPayAmount(multiply);
        baseMapper.insert(orderEntity);
        //保存订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setRealAmount(multiply);
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemService.save(orderItemEntity);
    }

    /**
     * 保存订单
     */
    private void saveOrder(OrderCreatTo orderCreatTo) {
        OrderEntity orderEntity = orderCreatTo.getOrderEntity();    //订单
        orderEntity.setCreateTime(new Date()); //创建时间
        baseMapper.insert(orderEntity); //保存订单
        List<OrderItemEntity> orderItemEntities = orderCreatTo.getOrderItemEntities(); //订单项
        orderItemService.saveBatch(orderItemEntities);  //保存订单项
    }

    /**
     * 创建订单
     */
    private OrderCreatTo CreatOrder() {
        OrderCreatTo orderCreatTo = new OrderCreatTo();
        //订单信息
        OrderEntity orderEntity = buildOrder();
        //获取所有订单项
        List<OrderItemEntity> orderItemEntities = buildOrderCartItems(orderEntity.getOrderSn());
        orderCreatTo.setOrderItemEntities(orderItemEntities);
        //计算价格
        computePrice(orderEntity, orderItemEntities);
        orderCreatTo.setOrderEntity(orderEntity);
        return orderCreatTo;
    }

    //验价
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        //订单总额 = 叠加订单项的总额
        BigDecimal totalPrice = new BigDecimal(0);
        BigDecimal promotionAmount = new BigDecimal(0);
        BigDecimal couponAmount = new BigDecimal(0);
        BigDecimal integrationAmount = new BigDecimal(0);
        Integer giftGrowth = 0;
        Integer giftIntegration = 0;
        for (OrderItemEntity entity : orderItemEntities) {
            totalPrice = totalPrice.add(entity.getRealAmount());
            promotionAmount = promotionAmount.add(entity.getPromotionAmount());
            couponAmount = couponAmount.add(entity.getCouponAmount());
            integrationAmount = integrationAmount.add(entity.getIntegrationAmount());
            giftGrowth = giftGrowth + entity.getGiftGrowth();   //总成长值
            giftIntegration = giftIntegration + entity.getGiftIntegration();//总积分
        }
        orderEntity.setTotalAmount(totalPrice); //订单总额
        orderEntity.setPayAmount(totalPrice.add(orderEntity.getFreightAmount())); //应付总额(加上运费)
        orderEntity.setPromotionAmount(promotionAmount);    //促销优化金额（促销价、满减、阶梯价）
        orderEntity.setCouponAmount(promotionAmount);   //优惠券抵扣金额
        orderEntity.setIntegrationAmount(promotionAmount);  //积分抵扣金额
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());    //订单支付状态
        orderEntity.setGrowth(giftGrowth);
        orderEntity.setIntegration(giftIntegration);
    }

    //构建订单项信息
    private List<OrderItemEntity> buildOrderCartItems(String orderSn) {
        //最后确定购物项价格
        List<OrderCartItemVo> userCartItem = cartFeignService.getUserCartItem(); //从购物车中重新获取
        return userCartItem.stream().map(item -> {
            OrderItemEntity orderItemEntity = buildOrderCartItem(item);
            assert orderItemEntity != null;
            orderItemEntity.setOrderSn(orderSn);    //订单号
            return orderItemEntity;
        }).collect(Collectors.toList());
    }

    //构建每一个订单项
    private OrderItemEntity buildOrderCartItem(OrderCartItemVo item) {
        //1.订单信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        //2.商品spu信息
        R spuInfoBySkuId = productFeignService.getSpuInfoBySkuId(item.getSkuId());
        SpuInfoVo spuInfoVo = spuInfoBySkuId.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfoVo.getId());
        orderItemEntity.setSpuName(spuInfoVo.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
        orderItemEntity.setSpuPic(item.getImage());
        orderItemEntity.setCategoryId(spuInfoVo.getCatalogId());
        //3.商品sku信息
        orderItemEntity.setSkuId(item.getSkuId());
        orderItemEntity.setSkuName(item.getTitle());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuQuantity(item.getCount());    //商品数量
        orderItemEntity.setSkuAttrsVals(JSONUtil.toJsonStr(item.getSkuAtr()));  //商品销售属性
        //4.商品优惠信息
        //5.积分信息
        orderItemEntity.setGiftGrowth((item.getPrice().multiply(new BigDecimal(item.getCount()))).intValue());  //赠送成长值
        orderItemEntity.setGiftIntegration((item.getPrice().multiply(new BigDecimal(item.getCount()))).intValue());  //赠送积分
        //6.订单项价格信息
        orderItemEntity.setPromotionAmount(new BigDecimal(0)); //促销分解金额
        orderItemEntity.setCouponAmount(new BigDecimal(0)); //优惠券优惠分解金额
        orderItemEntity.setIntegrationAmount(new BigDecimal(0));    //积分优惠分解金额
        //订单项实际金额
        BigDecimal orign = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));
        BigDecimal real = orign.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(real);    //订单总额(减去各种优惠)
        return orderItemEntity;
    }

    //构建订单基本信息
    private OrderEntity buildOrder() {
        //String timeId = IdWorker.getTimeId();
        long id = IdUtil.getSnowflake(1, 1).nextId();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(String.valueOf(id));//订单号
        //用户信息
        MemberRespVo memberRespVo = UserLoginInterceptor.loginUser.get();
        orderEntity.setMemberId(memberRespVo.getId()); //用户id
        orderEntity.setMemberUsername(memberRespVo.getUsername() != null ? memberRespVo.getUsername() : memberRespVo.getNickname()); //用户名
        //根据地址获取运费
        OrderSubmitVo orderSubmitVo = threadLocal.get(); //表单数据
        R fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareVo = fare.getData(new TypeReference<FareVo>() {
        });
        MemberAddressVo addressVo = fareVo.getMemberAddressVo();
        orderEntity.setFreightAmount(fareVo.getFare());//运费
        //收货人信息
        orderEntity.setBillReceiverPhone(addressVo.getPhone());//收件人电话
        orderEntity.setReceiverName(addressVo.getName());//收件人姓名
        orderEntity.setReceiverPostCode(addressVo.getPostCode());//邮政编码
        orderEntity.setReceiverProvince(addressVo.getProvince());//省份/直辖市
        orderEntity.setReceiverCity(addressVo.getCity());//城市
        orderEntity.setReceiverRegion(addressVo.getRegion());//区
        orderEntity.setReceiverDetailAddress(addressVo.getDetailAddress());//详细地址
        orderEntity.setNote(orderSubmitVo.getNote());//订单备注
        orderEntity.setPayType(orderSubmitVo.getPayType()); //支付方式
        orderEntity.setAutoConfirmDay(OrderConstant.ORDER_AUTO_CONFIRM_DAY); //自动确认订单天数
        orderEntity.setDeleteStatus(OrderDeleteEnum.NODELETE.getCode()); //订单是否删除
        return orderEntity;
    }

}